package xxl.signals.base 
{
	import flash.errors.IllegalOperationError;
	import flash.utils.Dictionary;
	import xxl.signals.core.IPrioritySignal;
	import xxl.signals.core.ISlot;
	import xxl.signals.util.SignalUtil;
	
	/**
	 * @author XXL
	 */
	public class PrioritySignal implements IPrioritySignal 
	{
		protected var _valueClasses:Array;
		
		protected var slots:Array = [];
		protected var sortFlag:Boolean = false;
		protected var slotCache:Dictionary = new Dictionary();
		
		public function PrioritySignal(...valueClasses) 
		{
			this.valueClasses = (valueClasses.length == 1 && valueClasses[0] is Array) ? valueClasses[0]:valueClasses;
		}
		
		protected function registerListenerWithPriority(listener:Function, once:Boolean = false, priority:int = 0):ISlot
		{
			if (SignalUtil.RegistrationPossible(slotCache, listener, once))
			{
				const newSlot:ISlot = new Slot(listener, this, once, priority);
				slots.push(newSlot);
				slotCache[listener] = newSlot;
				sortFlag = true;
				return newSlot;
			}
			return slotCache[listener];
		}
		
		/* INTERFACE xxl.signals.core.IPrioritySignal */
		
		public function addWithPriority(listener:Function, priority:int = 0):ISlot 
		{
			return registerListenerWithPriority(listener, false, priority);
		}
		
		public function addOnceWithPriority(listener:Function, priority:int = 0):ISlot 
		{
			return registerListenerWithPriority(listener, true, priority);
		}
		
		public function add(listener:Function):ISlot 
		{
			return registerListenerWithPriority(listener);
		}
		
		public function addOnce(listener:Function):ISlot 
		{
			return registerListenerWithPriority(listener, true);
		}
		
		public function removeAll():void 
		{
			slots.length = 0;
			slotCache = new Dictionary();
		}
		
		public function dispatch(...valueObjects):void 
		{
			SignalUtil.VerifyDispatch(valueClasses, valueObjects);
			
			if (sortFlag)
			{
				sortFlag = false;
				slots = slots.sortOn("priority", Array.NUMERIC);
			}
			
			for (var i:int = slots.length; i--; )
			{
				slots[i].execute(valueObjects);
			}
		}
		
		public function remove(listener:Function):ISlot 
		{
			const slot:ISlot = slotCache[listener];
			if (!slot)
			{
				return null;
			}
			SignalUtil.RemoveSlot(slots, listener);
			delete slotCache[listener];
			return slot;
		}
		
		public function get valueClasses():Array 
		{
			return _valueClasses;
		}
		
		public function set valueClasses(value:Array):void 
		{
			SignalUtil.VerifyValueClasses(value);
			_valueClasses = value ? value.concat() : [];
		}
		
		public function get numListeners():int 
		{
			return slots.length;
		}
		
	}

}